<?php
/** 
 * EGP Framework
 *
 * LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */

!defined('EGP_LIB_DIR') && die('Access Deny!');

/**
 * 分页处理
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */
class EGP_Paginator implements Countable, IteratorAggregate
{

    /**
     * URI获取当前页页码的关键字
     *
     * @var string
     */
    private $_uriSegment = 'page';

    /**
     * 平均每页显示记录数
     *
     * @var integer
     */
    private $_itemsPerPage = 20;

    /**
     * 当前页页码
     *
     * @var integer
     */
    private $_currentPage = null;

    /**
     * 记录总数
     *
     * @var integer
     */
    private $_itemsCount = null;

    /**
     * 当前页记录
     *
     * @var integer
     */
    private $_currentItems = null;

    /**
     * EGP_Db_Select
     *
     * @var EGP_Db_Select
     */
    private $_dbSelect = null;

    /**
     * 通过 EGP_Db_Select 获取到的记录总数
     *
     * @var integer
     */
    private $_dbSelectItemsCount = null;

    /**
     * 通过 EGP_Db_Select 获取到当前页的记录总数
     *
     * @var integer
     */
    private $_dbSelectCurrentItems = null;

    /**
     * 通过工厂方法快速获取 EGP_Paginator 对象
     *
     * @param  EGP_Db_Select  $select
     * @param  integer        $perPage
     * @param  string         $uriSegment
     * @return EGP_Paginator
     */
    public static function factory(EGP_Db_Select $select, $perPage = 20, $uriSegment = 'page')
    {
        $paginator = new EGP_Paginator();
        $paginator->setDbSelect($select)
            ->setItemsPerPage($perPage)
            ->setUriSegment($uriSegment);
        return $paginator;
    }

    /**
     * 设置URI获取当前页页码的关键字
     *
     * @param  string  $uriSegment
     * @return EGP_Paginator
     */
    public function setUriSegment($uriSegment)
    {
        $this->_uriSegment = (string) $uriSegment;
        return $this;
    }

    /**
     * 获取URI获取当前页页码的关键字
     *
     * @return string
     */
    public function getUriSegment()
    {
        return $this->_uriSegment;
    }

    /**
     * 设置平均每页显示记录数
     *
     * @param  integer  $perPage
     * @return EGP_Paginator
     */
    public function setItemsPerPage($perPage)
    {
        $this->_itemsPerPage = $perPage < 1 ? 1 : (integer) $perPage;
        return $this;
    }

    /**
     * 获取平均每页显示记录数
     *
     * @return integer
     */
    public function getItemsPerPage()
    {
        return $this->_itemsPerPage;
    }

    /**
     * 设置当前页页码
     *
     * @param  integer  $currentPage
     * @return EGP_Paginator
     */
    public function setCurrentPage($currentPage)
    {
        $this->_currentPage = $currentPage < 1 ? 1 : (integer) $currentPage;
        return $this;
    }

    /**
     * 获取当前页页码
     *
     * @return integer
     */
    public function getCurrentPage()
    {
        if (null === $this->_currentPage) {
            $request = EGP_Http_Request::getInstance();
            $currentPage = $request->getParam($this->getUriSegment(), 1);
            $currentPage > $this->getPagesCount() && $currentPage = $this->getPagesCount();
            $this->setCurrentPage($currentPage);

        }
        return $this->_currentPage;
    }

    /**
     * 获得页总数
     *
     * @return integer
     */
    public function getPagesCount()
    {
        return ceil($this->getItemsCount() / $this->getItemsPerPage());
    }

    /**
     * 设置记录总数
     *
     * @param  integer  $count
     * @return EGP_Paginator
     */
    public function setItemsCount($count)
    {
        $this->_itemsCount = $count < 0 ? 0 : (integer) $count;
        return $this;
    }

    /**
     * 获取记录总数
     *
     * @return integer
     */
    public function getItemsCount()
    {
        return (integer) $this->_itemsCount;
    }

    /**
     * 设置当前页的数据
     *
     * @param  array  $items
     * @return EGP_Paginator
     */
    public function setCurrentItems(array $items)
    {
        $this->_currentItems = $items;
        return $this;
    }

    /**
     * 获取当前页的数据
     *
     * @return array
     */
    public function getCurrentItems()
    {
        if (null === $this->_currentItems) {
            $this->_currentItems = ($this->_dbSelect instanceof EGP_Db_Select) ?
                                    $this->_getDbSelectCurrentItems() :
                                    array();
        }
        return $this->_currentItems;
    }

    /**
     * 获取当前页数据总数
     *
     * @return integer
     */
    public function getCurrentItemsCount()
    {
        return count($this->getCurrentItems());
    }

    /**
     * 设置 EGP_Db_Select
     *
     * @param  EGP_Db_Select  $select
     * @return EGP_Paginator
     */
    public function setDbSelect(EGP_Db_Select $select)
    {
        $this->_dbSelect = $select;
        return $this;
    }

    /**
     * 获取 EGP_Db_Select 对象
     *
     * @return EGP_Db_Select
     */
    public function getDbSelect()
    {
        return $this->_dbSelect;
    }

    /**
     * 从 EGP_Db_Select 获取数据总数
     *
     * @return integer
     */
    private function _getDbSelectItemsCount()
    {
        if (null === $this->_dbSelectItemsCount) {
            $select = clone $this->_dbSelect;
            if (true === $select->getPart(EGP_Db_Select::DISTINCT)) {
                $columnParts = $select->getPart(EGP_Db_Select::COLUMNS);
                $columns = array();
                foreach ($columnParts as $part)
                    $columns[] = $part[1];
                if (count($columns) == 1 && $columns[0] == '*')
                    $groupPart = null;
                else
                    $groupPart = implode(',', $columns);
            } else {
                $groupPart = implode(',', $select->getPart(EGP_Db_Select::GROUP));
            }
            $countPart  = empty($groupPart) ? 'COUNT(0)' : 'COUNT(DISTINCT ' . $groupPart . ')';
            $select->reset(EGP_Db_Select::COLUMNS)
                   ->reset(EGP_Db_Select::ORDER)
                   ->reset(EGP_Db_Select::LIMIT_OFFSET)
                   ->reset(EGP_Db_Select::GROUP)
                   ->reset(EGP_Db_Select::DISTINCT)
                   ->columns($countPart);
            $this->_dbSelectItemsCount = (integer) $select->fetchOne();
        }
        return $this->_dbSelectItemsCount;
    }

    /**
     * 从 EGP_Db_Select 获取当前页数据
     *
     * @return array
     */
    protected function _getDbSelectCurrentItems()
    {
        if (null === $this->_dbSelectCurrentItems) {
            $this->setItemsCount($this->_getDbSelectItemsCount());
            $items = $this->_dbSelect->limitPage($this->getCurrentPage(), $this->getItemsPerPage())
                                     ->fetchAll();
            $this->_dbSelectCurrentItems = (array) $items;
        }
        return $this->_dbSelectCurrentItems;
    }

    /**
     * 返回给 foreach 调用的接口
     *
     * @return ArrayObject
     */
    public function getIterator()
    {
        return new ArrayObject($this->getCurrentItems());
    }

    /**
     * 返回 count 函数获得的值
     *
     * @return integer
     */
    public function count()
    {
        return $this->getCurrentItemsCount();
    }

    /**
     * 调用分页样式
     *
     * @param  string  $style   样式名称
     * @param  array   $params  需要传递的参数
     * @return string
     */
    public function output($style = 'Classic', array $params = array())
    {
        $this->count(); // FIX BUG

        $class = 'EGP_Paginator_Style_' . ucfirst($style);
        if (!EGP_Core::classExists($class))
            throw new EGP_Exception("分页样式 '$style' 不存在");

        $obj = new $class($this, $params);
        if (!$obj instanceof EGP_Paginator_Style_Abstract)
            throw new EGP_Exception("分页样式 '$class' 必须从 'EGP_Paginator_Style_Abstract' 继承");

        return $obj;
    }

}